/*
 * w5200.c
 *
 *  Created on: 2014-3-9
 *      Author: Rome
 */
#include "w5200.h"
#include "system.h"
#include "altera_avalon_spi.h"
#include "at24c16.h"
#include <stdio.h>
#include "unistd.h"
#include "helper.h"
// alt_u8 pOperationBuf[512];

volatile alt_u32 tx_offset = 0, tx1_offset = 0;

void ResetW5200()
{

}

alt_32 W5200CheckLink(void)
{
	alt_u8 check_link_pOperationBuf[16];

	W5200SpiRead(W5200_PHY_STATUS, 1, &check_link_pOperationBuf[0]);

	if((check_link_pOperationBuf[0] & 0x00000020) == 0)	// 0x00000020 link bit, 1:link, 0:nolink
	{
		printf_c("Check Link Failed\n");
		return -1;
	}

	return 0;
}

void W5200CloseSocket(alt_u8 socket_n)
{
	alt_u8 CloseSocket_pOperationBuf[2];
	CloseSocket_pOperationBuf[0] = Sn_CR_CLOSE;
	W5200SpiWrite(W5200_SN_CR + socket_n*0x100, 1, &CloseSocket_pOperationBuf[0]);
	return;
}

alt_32 W5200CheckEstablish(alt_32 DealyTime, alt_u8 socket_n)
{
	alt_u32 cycles = 0x50;
	alt_u8 est_pOperationBuf[128];
	alt_u32 i=0;
//	printf("Entring check %d connection...\n", socket_n);

	while(cycles--)
	{
		usleep(DealyTime);

		// read status
		W5200SpiRead(W5200_SN_SR + socket_n*0x100, 1, &est_pOperationBuf[0]);
		// socket closed
		if(est_pOperationBuf[0] == 0)
		{
//			printf_c("Connection closed!\n");
			return W5200RT_CLOSED;
		}

		// socket 0 interrupt comes,check it
		W5200SpiRead(W5200_SN_IR + socket_n*0x100, 1, &est_pOperationBuf[0]);

		// check if it is a connect
		if((est_pOperationBuf[0] & 0x01) != 0)
		{
			// clear socket IR interrupt status
			est_pOperationBuf[0] = 0;
			W5200SpiWrite(W5200_SN_IR + socket_n*0x100, 1, &est_pOperationBuf[0]);

			// clear global IR interrupt status
			W5200SpiWrite(W5200_IR2 + socket_n*0x100, 1, &est_pOperationBuf[0]);

			// connect established
//			printf_c("Connection established!\n");
			return W5200RT_SUCCESS;
		}else
		{
			if (i%1000 == 0)
			{
				W5200SpiRead(W5200_SN_SR, 1, &est_pOperationBuf[0]);
//				printf_c("%d, read status before connect established: 0x%x!\n", i, est_pOperationBuf[0]);
			}
			++i;
		}
	}

//	printf_c("Connection timeout!\n");
	return W5200RT_TIMEOUT;
}
void W5200GlobalInit()
{
	alt_u8 initBuf[128];

	// test read w5200 version
	W5200SpiRead(W5200_VERISON, 1, &initBuf[0]);
	//printf("w5200 version: 0x%d!\n", initBuf[0]);

	// first reset w5200, it will left reset status in 150ms by itself
	initBuf[0] = W5200_RESET;
	W5200SpiWrite(W5200_MR, 1, &initBuf[0]);

	usleep(150 * 1000);

	// read information from at24c16
	AcquireInitialInformation(&initBuf[0]);

	// write gateway, mask, mac address, ip
	W5200SpiWrite(W5200_GAR, 18, &initBuf[0]);
}
alt_32 W5200Initial(alt_32 DealyTime, alt_u8 socket_n)
{
	alt_u32 i=0;
	alt_u8 initBuf[128];

//	// test read w5200 version
//	W5200SpiRead(W5200_VERISON, 1, &initBuf[0]);
//	//printf("w5200 version: 0x%d!\n", initBuf[0]);
//
//	// first reset w5200, it will left reset status in 150ms by itself
//	initBuf[0] = W5200_RESET;
//	W5200SpiWrite(W5200_MR, 1, &initBuf[0]);
//
//	usleep(150 * 1000);
//
//	// read information from at24c16
//	AcquireInitialInformation(&initBuf[0]);
//
//	// write gateway, mask, mac address, ip
//	W5200SpiWrite(W5200_GAR, 18, &initBuf[0]);

	// config port to 1080 = 0x438
	initBuf[0] = 0x4;
	initBuf[1] = 0x38;
	initBuf[0] = (W5200_PORT & 0xFF00) >> 8;
	initBuf[1] = W5200_PORT & 0xFF;
	
	W5200SpiWrite(W5200_SN_PORT + socket_n*0x100, 2, &initBuf[0]);

	// write imr2 1 to enable socket 0 interrupt
	initBuf[0] = 0xFF;  //enable all
	W5200SpiWrite(W5200_IMR2, 1, &initBuf[0]);

	// config mr register
	initBuf[0] = 0x61;
	W5200SpiWrite(W5200_SN_MR + socket_n*0x100, 1, &initBuf[0]);

	// initial server
	initBuf[0] = 0x01;
	W5200SpiWrite(W5200_SN_CR + socket_n*0x100, 1, &initBuf[0]);

	// check init done
	while(1)
	{
		if(i%1000 == 0)
			printf_c("while(1): looped %d times\n", i);

		W5200SpiRead(W5200_SN_SR + socket_n*0x100, 1, &initBuf[0]);

//		usleep(DealyTime);

//		printf_c("w5200 socket status : 0x%x\n", initBuf[0]);

		if(initBuf[0] == 0x13 || initBuf[0] == 0x17)
		{
			printf_c("w5200 socket %d initial done..\n", socket_n);
			break;
		}
		else if(initBuf[0] == 0x00)
		{
			printf_c("while(1): w5200 socket closed, quit!\n");
			return W5200RT_CLOSED;
		}
		i++;
		if((i > 0x10000)/* || (W5200CheckLink() < 0)*/)
			return W5200RT_TIMEOUT;
	}

	// listen
	initBuf[0] = 0x02;
	W5200SpiWrite(W5200_SN_CR + socket_n*0x100, 1, &initBuf[0]);

	usleep(100);

	W5200SpiRead(W5200_SN_SR + socket_n*0x100, 1, &initBuf[0]);

//	printf_c("w5200 socket status : 0x%x\n", initBuf[0]);

	if(initBuf[0] == 0x00)
	{
		printf_c("exiting W5200Initial w5200 socket closed, quit!\n");
		return W5200RT_CLOSED;
	}

	//printf("w5200 socket begin to listen.\n");

	return W5200RT_INITED;
}

alt_32 W5200CheckRecv(alt_u8 socket_n)
{
	alt_u8 check_recv_pOperationBuf[16];
	alt_32 rev_cnt;

	W5200SpiRead(W5200_SN_SR + socket_n*0x100, 1, &check_recv_pOperationBuf[0]);

	if(check_recv_pOperationBuf[0] == 0 || check_recv_pOperationBuf[0] == 28)
		return W5200RT_RBUF_NA;

	W5200SpiRead(W5200_SN_RX_RSR + socket_n*0x100, 2, &check_recv_pOperationBuf[0]);

	rev_cnt = check_recv_pOperationBuf[0] * 256 + check_recv_pOperationBuf[1];

	return rev_cnt;
}

alt_32 W5200RecvBuf(alt_u8* Pbuf, alt_u32 length, alt_u8 socket_n)
{
	alt_u32 rx_offset,rx1_offset;
	alt_u32 i;
	alt_u8 recv_pOperationBuf[16];

	W5200SpiRead(W5200_SN_RX_RD + socket_n*0x100, 2, &recv_pOperationBuf[0]);

	rx_offset = 256 * recv_pOperationBuf[0] + recv_pOperationBuf[1];

	rx1_offset=rx_offset;

	rx_offset=rx_offset&(RX_MEM_SIZE-1);

	if((length + rx_offset) <= RX_MEM_SIZE)
		W5200SpiRead((RX_MEM + 0x800*socket_n + rx_offset), length, Pbuf);
	else
	{
		i = RX_MEM_SIZE - rx_offset;
		W5200SpiRead((RX_MEM + 0x800*socket_n + rx_offset), i, Pbuf);
		W5200SpiRead((RX_MEM + 0x800*socket_n), (length - i), Pbuf + i);
	}

	rx1_offset += length;
	recv_pOperationBuf[0] = rx1_offset / 256;
	recv_pOperationBuf[1] = rx1_offset & 0x00FF;
	W5200SpiWrite(W5200_SN_RX_RD + socket_n*0x100, 2, &recv_pOperationBuf[0]);
	recv_pOperationBuf[0] = W5200_RECV;
	W5200SpiWrite(W5200_SN_CR + socket_n*0x100, 1, &recv_pOperationBuf[0]);

	return 0;
}

alt_32 W5200SendBuf(alt_u8* Pbuf, alt_u32 length, alt_u8 socket_n)
{
	int i;
	int cycles = 0x1000;

	alt_u8 send_pOperationBuf[16];

	W5200SpiRead(W5200_SN_TX_WR + socket_n*0x100, 2, &send_pOperationBuf[0]);
	tx_offset = 256 * send_pOperationBuf[0] + send_pOperationBuf[1];

	// store tx offset
	tx1_offset = tx_offset;

	tx_offset = tx_offset&(TX_MEM_SIZE-1);

	if((tx_offset + length) <= TX_MEM_SIZE)
		W5200SpiWrite((TX_MEM + 0x800*socket_n + tx_offset), length, Pbuf);
	else
	{
		i = TX_MEM_SIZE - tx_offset;
		W5200SpiWrite((TX_MEM + 0x800*socket_n + tx_offset), i, Pbuf);
		W5200SpiWrite((TX_MEM + 0x800*socket_n), (length - i), Pbuf + i);
	}

	tx1_offset+=length;

	send_pOperationBuf[0] = tx1_offset / 256;
	send_pOperationBuf[1] = tx1_offset & 0x00FF;

	W5200SpiWrite(W5200_SN_TX_WR + socket_n*0x100, 2, &send_pOperationBuf[0]);

	// send data
	send_pOperationBuf[0] = W5200_SEND;
	W5200SpiWrite(W5200_SN_CR + socket_n*0x100, 1, &send_pOperationBuf[0]);

	//check send done
	do{
		W5200SpiRead(W5200_SN_IR + socket_n*0x100, 1, &send_pOperationBuf[0]);
		if(send_pOperationBuf[0]&Sn_IR_SEND_OK)
			return W5200RT_SUCCESS;
		else if(send_pOperationBuf[0]&Sn_IR_TIMEOUT)
			return W5200RT_TIMEOUT;
	}while(cycles--);

	return W5200RT_TIMEOUT;
}

void W5200SpiWrite(alt_u32 Address, alt_u32 Length, alt_u8 * pbuf)
{

	alt_u8 WriteAddressLength[4];
	alt_u8 ReadBufTemp[4];

	// address msb first
	WriteAddressLength[0] = (Address & 0x0000FF00) >> 8;
	WriteAddressLength[1] =  Address & 0x000000FF;

	// length msb first, | 0x00008000 indicate write operation
	WriteAddressLength[2] = ((Length & 0x00007F00) | 0x00008000) >> 8;
	WriteAddressLength[3] =  Length & 0x000000FF;

	// write address and legnth first
	alt_avalon_spi_command(SPI_0_BASE,
							0,	// slave 0
							4,	// 4 bytes
							(const alt_u8 *)(&WriteAddressLength[0]),
							0,	// read length
							(alt_u8 * )(&ReadBufTemp[0]),
							ALT_AVALON_SPI_COMMAND_MERGE);// keep cs_n active after write

	// write data
	alt_avalon_spi_command(SPI_0_BASE,
							0,	// slave 0
							Length,
							(const alt_u8 *)pbuf,
							0,
							(alt_u8 * )(&ReadBufTemp[0]),
							ALT_AVALON_SPI_COMMAND_TOGGLE_SS_N);// keep cs_n active after write
	return;
}

void W5200SpiRead(alt_u32 Address, alt_u32 Length, alt_u8 * pbuf)
{

	alt_u8 WriteAddressLength[4];
	alt_u8 WriteBufTemp[4];
	// address msb first
	WriteAddressLength[0] = (Address & 0x0000FF00) >> 8;
	WriteAddressLength[1] =  Address & 0x000000FF;
	// length msb first
	WriteAddressLength[2] = (Length & 0x00007F00) >> 8;
	WriteAddressLength[3] =  Length & 0x000000FF;

	// write address and legnth first
	alt_avalon_spi_command(SPI_0_BASE,
							0,	// slave 0
							4,	// 4 bytes
							(const alt_u8 *)(&WriteAddressLength[0]),
							0,	// read length
							(alt_u8 * )pbuf,
							ALT_AVALON_SPI_COMMAND_MERGE);// keep cs_n active after write

	// write data
	alt_avalon_spi_command(SPI_0_BASE,
							0,	// slave 0
							0,	// write 0 bytes
							(const alt_u8 *)WriteBufTemp,
							Length,
							(alt_u8 * )pbuf,
							ALT_AVALON_SPI_COMMAND_TOGGLE_SS_N);// keep cs_n active after write
	return;
}


